Completed
Pull Request — master (#91)
by thomas
01:37
created

APIClient.transactions   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
dl 0
loc 5
rs 9.4285
nop 2
1
/* globals onLoadWorkerLoadAsmCrypto */
2
3
var _ = require('lodash'),
4
    q = require('q'),
5
    bitcoin = require('bitcoinjs-lib'),
6
    bitcoinMessage = require('bitcoinjs-message'),
7
8
    bip39 = require("bip39"),
9
    Wallet = require('./wallet'),
10
    RestClient = require('./rest_client'),
11
    Encryption = require('./encryption'),
12
    KeyDerivation = require('./keyderivation'),
13
    EncryptionMnemonic = require('./encryption_mnemonic'),
14
    blocktrail = require('./blocktrail'),
15
    randomBytes = require('randombytes'),
16
    CryptoJS = require('crypto-js'),
17
    webworkifier = require('./webworkifier');
18
19
var useWebWorker = require('./use-webworker')();
20
21
/**
22
 * Bindings to conssume the BlockTrail API
23
 *
24
 * @param options       object{
25
 *                          apiKey: 'API_KEY',
26
 *                          apiSecret: 'API_SECRET',
27
 *                          host: 'defaults to api.blocktrail.com',
28
 *                          network: 'BTC|LTC',
29
 *                          testnet: true|false
30
 *                      }
31
 * @constructor
32
 */
33
var APIClient = function(options) {
34
    var self = this;
35
36
    // handle constructor call without 'new'
37
    if (!(this instanceof APIClient)) {
38
        return new APIClient(options);
39
    }
40
    self.bitcoinCash = options.network && options.network === "BCC";
41
    self.testnet = options.testnet = options.testnet || false;
42
    if (self.bitcoinCash) {
43
        if (self.testnet) {
44
            self.network = bitcoin.networks.bitcoincashtestnet;
45
        } else {
46
            self.network = bitcoin.networks.bitcoincash;
47
        }
48
    } else {
49
        if (self.testnet) {
50
            self.network = bitcoin.networks.testnet;
51
        } else {
52
            self.network = bitcoin.networks.bitcoin;
53
        }
54
    }
55
56
    self.feeSanityCheck = typeof options.feeSanityCheck !== "undefined" ? options.feeSanityCheck : true;
57
    self.feeSanityCheckBaseFeeMultiplier = options.feeSanityCheckBaseFeeMultiplier || 200;
58
59
    options.apiNetwork = options.apiNetwork || ((self.testnet ? "t" : "") + (options.network || 'BTC').toUpperCase());
60
61
    /**
62
     * @type RestClient
63
     */
64
    self.client = APIClient.initRestClient(options);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
65
};
66
67
APIClient.initRestClient = function(options) {
68
    // BLOCKTRAIL_SDK_API_ENDPOINT overwrite for development
69
    if (process.env.BLOCKTRAIL_SDK_API_ENDPOINT) {
70
        options.host = process.env.BLOCKTRAIL_SDK_API_ENDPOINT;
71
    }
72
73
    // trim off leading https?://
74
    if (options.host && options.host.indexOf("https://") === 0) {
75
        options.https = true;
76
        options.host = options.host.substr(8);
77
    } else if (options.host && options.host.indexOf("http://") === 0) {
78
        options.https = false;
79
        options.host = options.host.substr(7);
80
    }
81
82
    if (typeof options.https === "undefined") {
83
        options.https = true;
84
    }
85
86
    if (!options.host) {
87
        options.host = 'api.blocktrail.com';
88
    }
89
90
    if (!options.port) {
91
        options.port = options.https ? 443 : 80;
92
    }
93
94
    if (!options.endpoint) {
95
        options.endpoint = "/" + (options.apiVersion || "v1") + (options.apiNetwork ? ("/" + options.apiNetwork) : "");
96
    }
97
98
    return new RestClient(options);
99
};
100
101
var determineDataStorageV2_3 = function(options) {
102
    return q.when(options)
103
        .then(function(options) {
104
            // legacy
105
            if (options.storePrimaryMnemonic) {
106
                options.storeDataOnServer = options.storePrimaryMnemonic;
107
            }
108
109
            // storeDataOnServer=false when primarySeed is provided
110
            if (typeof options.storeDataOnServer === "undefined") {
111
                options.storeDataOnServer = !options.primarySeed;
112
            }
113
114
            return options;
115
        });
116
};
117
118
var produceEncryptedDataV2 = function(options, notify) {
119
    return q.when(options)
120
        .then(function(options) {
121
            if (options.storeDataOnServer) {
122
                if (!options.secret) {
123
                    if (!options.passphrase) {
124
                        throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
125
                    }
126
127
                    notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
128
129
                    options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
130
                    options.encryptedSecret = CryptoJS.AES.encrypt(options.secret, options.passphrase).toString(CryptoJS.format.OpenSSL); // 'base64' string
131
                }
132
133
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
134
135
                options.encryptedPrimarySeed = CryptoJS.AES.encrypt(options.primarySeed.toString('base64'), options.secret)
136
                    .toString(CryptoJS.format.OpenSSL); // 'base64' string
137
                options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
138
139
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
140
141
                options.recoveryEncryptedSecret = CryptoJS.AES.encrypt(options.secret, options.recoverySecret)
142
                                                              .toString(CryptoJS.format.OpenSSL); // 'base64' string
143
            }
144
145
            return options;
146
        });
147
};
148
149
APIClient.prototype.promisedEncrypt = function(pt, pw, iter) {
150
    if (useWebWorker && typeof onLoadWorkerLoadAsmCrypto === "function") {
151
        // generate randomness outside of webworker because many browsers don't have crypto.getRandomValues inside webworkers
152
        var saltBuf = Encryption.generateSalt();
153
        var iv = Encryption.generateIV();
154
155
        return webworkifier.workify(APIClient.prototype.promisedEncrypt, function factory() {
156
            return require('./webworker');
157
        }, onLoadWorkerLoadAsmCrypto, {
158
            method: 'Encryption.encryptWithSaltAndIV',
159
            pt: pt,
160
            pw: pw,
161
            saltBuf: saltBuf,
162
            iv: iv,
163
            iterations: iter
164
        })
165
            .then(function(data) {
166
                return Buffer.from(data.cipherText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
167
            });
168
    } else {
169
        try {
170
            return q.when(Encryption.encrypt(pt, pw, iter));
171
        } catch (e) {
172
            return q.reject(e);
173
        }
174
    }
175
};
176
177
APIClient.prototype.promisedDecrypt = function(ct, pw) {
178
    if (useWebWorker && typeof onLoadWorkerLoadAsmCrypto === "function") {
179
        return webworkifier.workify(APIClient.prototype.promisedDecrypt, function() {
180
            return require('./webworker');
181
        }, onLoadWorkerLoadAsmCrypto, {
182
            method: 'Encryption.decrypt',
183
            ct: ct,
184
            pw: pw
185
        })
186
            .then(function(data) {
187
                return Buffer.from(data.plainText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
188
            });
189
    } else {
190
        try {
191
            return q.when(Encryption.decrypt(ct, pw));
192
        } catch (e) {
193
            return q.reject(e);
194
        }
195
    }
196
};
197
198
APIClient.prototype.produceEncryptedDataV3 = function(options, notify) {
199
    var self = this;
200
201
    return q.when(options)
202
        .then(function(options) {
203
            if (options.storeDataOnServer) {
204
                return q.when()
205
                    .then(function() {
206
                        if (!options.secret) {
207
                            if (!options.passphrase) {
208
                                throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
209
                            }
210
211
                            notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
212
213
                            // -> now a buffer
214
                            options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
215
216
                            // -> now a buffer
217
                            return self.promisedEncrypt(options.secret, new Buffer(options.passphrase), KeyDerivation.defaultIterations)
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
218
                                .then(function(encryptedSecret) {
219
                                    options.encryptedSecret = encryptedSecret;
220
                                });
221
                        } else {
222
                            if (!(options.secret instanceof Buffer)) {
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Complexity Best Practice introduced by
There is no return statement if !(options.secret instanceof Buffer) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
223
                                throw new Error('Secret must be a buffer');
224
                            }
225
                        }
226
                    })
227
                    .then(function() {
228
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
229
230
                        return self.promisedEncrypt(options.primarySeed, options.secret, KeyDerivation.subkeyIterations)
231
                            .then(function(encryptedPrimarySeed) {
232
                                options.encryptedPrimarySeed = encryptedPrimarySeed;
233
                            });
234
                    })
235
                    .then(function() {
236
                        // skip generating recovery secret when explicitly set to false
237
                        if (options.recoverySecret === false) {
238
                            return;
239
                        }
240
241
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
242
                        if (!options.recoverySecret) {
243
                            options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
244
                        }
245
246
                        return self.promisedEncrypt(options.secret, options.recoverySecret, KeyDerivation.defaultIterations)
247
                            .then(function(recoveryEncryptedSecret) {
248
                                options.recoveryEncryptedSecret = recoveryEncryptedSecret;
249
                            });
250
                    })
251
                    .then(function() {
252
                        return options;
253
                    });
254
            } else {
255
                return options;
256
            }
257
        });
258
};
259
260
var doRemainingWalletDataV2_3 = function(options, network, notify) {
261
    return q.when(options)
262
        .then(function(options) {
263
            if (!options.backupPublicKey) {
264
                options.backupSeed = options.backupSeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
265
            }
266
267
            notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
268
269
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
270
271
            notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
272
273
            if (!options.backupPublicKey) {
274
                options.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.backupSeed, network);
275
                options.backupPublicKey = options.backupPrivateKey.neutered();
276
            }
277
278
            options.primaryPublicKey = options.primaryPrivateKey.deriveHardened(options.keyIndex).neutered();
279
280
            notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
281
282
            return options;
283
        });
284
};
285
286
APIClient.prototype.mnemonicToPrivateKey = function(mnemonic, passphrase, cb) {
287
    var self = this;
288
289
    var deferred = q.defer();
290
    deferred.promise.spreadNodeify(cb);
291
292
    deferred.resolve(q.fcall(function() {
293
        return self.mnemonicToSeedHex(mnemonic, passphrase).then(function(seedHex) {
294
            return bitcoin.HDNode.fromSeedHex(seedHex, self.network);
295
        });
296
    }));
297
298
    return deferred.promise;
299
};
300
301
APIClient.prototype.mnemonicToSeedHex = function(mnemonic, passphrase) {
302
    var self = this;
303
304
    if (useWebWorker) {
305
        return webworkifier.workify(self.mnemonicToSeedHex, function() {
306
            return require('./webworker');
307
        }, {method: 'mnemonicToSeedHex', mnemonic: mnemonic, passphrase: passphrase})
308
            .then(function(data) {
309
                return data.seed;
310
            });
311
    } else {
312
        try {
313
            return q.when(bip39.mnemonicToSeedHex(mnemonic, passphrase));
314
        } catch (e) {
315
            return q.reject(e);
316
        }
317
    }
318
};
319
320
APIClient.prototype.resolvePrimaryPrivateKeyFromOptions = function(options, cb) {
321
    var self = this;
322
323
    var deferred = q.defer();
324
    deferred.promise.nodeify(cb);
325
326
    try {
327
        // avoid conflicting options
328
        if (options.passphrase && options.password) {
329
            throw new blocktrail.WalletCreateError("Can't specify passphrase and password");
330
        }
331
        // normalize passphrase/password
332
        options.passphrase = options.passphrase || options.password;
333
        delete options.password;
334
335
        // avoid conflicting options
336
        if (options.primaryMnemonic && options.primarySeed) {
337
            throw new blocktrail.WalletInitError("Can only specify one of; Primary Mnemonic or Primary Seed");
338
        }
339
340
        // avoid deprecated options
341
        if (options.primaryPrivateKey) {
342
            throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
343
        }
344
345
        // make sure we have at least one thing to use
346
        if (!options.primaryMnemonic && !options.primarySeed) {
347
            throw new blocktrail.WalletInitError("Need to specify at least one of; Primary Mnemonic or Primary Seed");
348
        }
349
350
        if (options.primarySeed) {
351
            self.primarySeed = options.primarySeed;
352
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(self.primarySeed, self.network);
353
            deferred.resolve(options);
354
        } else {
355
            if (!options.passphrase) {
356
                throw new blocktrail.WalletInitError("Can't init wallet with Primary Mnemonic without a passphrase");
357
            }
358
359
            self.mnemonicToSeedHex(options.primaryMnemonic, options.passphrase)
360
                .then(function(seedHex) {
361
                    try {
362
                        options.primarySeed = new Buffer(seedHex, 'hex');
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
363
                        options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, self.network);
364
                        deferred.resolve(options);
365
                    } catch (e) {
366
                        deferred.reject(e);
367
                    }
368
                }, function(e) {
369
                    deferred.reject(e);
370
                });
371
        }
372
    } catch (e) {
373
        deferred.reject(e);
374
    }
375
376
    return deferred.promise;
377
};
378
379
APIClient.prototype.resolveBackupPublicKeyFromOptions = function(options, cb) {
380
    var self = this;
381
382
    var deferred = q.defer();
383
    deferred.promise.nodeify(cb);
384
385
    try {
386
        // avoid conflicting options
387
        if (options.backupMnemonic && options.backupPublicKey) {
388
            throw new blocktrail.WalletInitError("Can only specify one of; Backup Mnemonic or Backup PublicKey");
389
        }
390
391
        // make sure we have at least one thing to use
392
        if (!options.backupMnemonic && !options.backupPublicKey) {
393
            throw new blocktrail.WalletInitError("Need to specify at least one of; Backup Mnemonic or Backup PublicKey");
394
        }
395
396
        if (options.backupPublicKey) {
397
            if (options.backupPublicKey instanceof bitcoin.HDNode) {
398
                deferred.resolve(options);
399
            } else {
400
                options.backupPublicKey = bitcoin.HDNode.fromBase58(options.backupPublicKey, self.network);
401
                deferred.resolve(options);
402
            }
403
        } else {
404
            self.mnemonicToPrivateKey(options.backupMnemonic, "").then(function(backupPrivateKey) {
405
                options.backupPublicKey = backupPrivateKey.neutered();
406
                deferred.resolve(options);
407
            }, function(e) {
408
                deferred.reject(e);
409
            });
410
        }
411
    } catch (e) {
412
        deferred.reject(e);
413
    }
414
415
    return deferred.promise;
416
};
417
418
APIClient.prototype.debugAuth = function(cb) {
419
    var self = this;
420
421
    return self.client.get("/debug/http-signature", null, true, cb);
422
};
423
424
/**
425
 * get a single address
426
 *
427
 * @param address      string       address hash
428
 * @param [cb]          function    callback function to call when request is complete
429
 * @return q.Promise
430
 */
431
APIClient.prototype.address = function(address, cb) {
432
    var self = this;
433
434
    return self.client.get("/address/" + address, null, cb);
435
};
436
437
APIClient.prototype.addresses = function(addresses, cb) {
438
    var self = this;
439
440
    return self.client.post("/address", null, {"addresses": addresses}, cb);
441
};
442
443
/**
444
 * get all transactions for an address (paginated)
445
 *
446
 * @param address       string      address hash
447
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
448
 * @param [cb]          function    callback function to call when request is complete
449
 * @return q.Promise
450
 */
451
APIClient.prototype.addressTransactions = function(address, params, cb) {
452
    var self = this;
453
454
    if (typeof params === "function") {
455
        cb = params;
456
        params = null;
457
    }
458
459
    return self.client.get("/address/" + address + "/transactions", params, cb);
460
};
461
462
/**
463
 * get all transactions for a batch of addresses (paginated)
464
 *
465
 * @param addresses     array       address hashes
466
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
467
 * @param [cb]          function    callback function to call when request is complete
468
 * @return q.Promise
469
 */
470
APIClient.prototype.batchAddressHasTransactions = function(addresses, params, cb) {
471
    var self = this;
472
473
    if (typeof params === "function") {
474
        cb = params;
475
        params = null;
476
    }
477
478
    return self.client.post("/address/has-transactions", params, {"addresses": addresses}, cb);
479
};
480
481
/**
482
 * get all unconfirmed transactions for an address (paginated)
483
 *
484
 * @param address       string      address hash
485
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
486
 * @param [cb]          function    callback function to call when request is complete
487
 * @return q.Promise
488
 */
489
APIClient.prototype.addressUnconfirmedTransactions = function(address, params, cb) {
490
    var self = this;
491
492
    if (typeof params === "function") {
493
        cb = params;
494
        params = null;
495
    }
496
497
    return self.client.get("/address/" + address + "/unconfirmed-transactions", params, cb);
498
};
499
500
/**
501
 * get all unspent outputs for an address (paginated)
502
 *
503
 * @param address       string      address hash
504
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
505
 * @param [cb]          function    callback function to call when request is complete
506
 * @return q.Promise
507
 */
508
APIClient.prototype.addressUnspentOutputs = function(address, params, cb) {
509
    var self = this;
510
511
    if (typeof params === "function") {
512
        cb = params;
513
        params = null;
514
    }
515
516
    return self.client.get("/address/" + address + "/unspent-outputs", params, cb);
517
};
518
519
/**
520
 * get all unspent outputs for a batch of addresses (paginated)
521
 *
522
 * @param addresses     array       address hashes
523
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
524
 * @param [cb]          function    callback function to call when request is complete
525
 * @return q.Promise
526
 */
527
APIClient.prototype.batchAddressUnspentOutputs = function(addresses, params, cb) {
528
    var self = this;
529
530
    if (typeof params === "function") {
531
        cb = params;
532
        params = null;
533
    }
534
535
    return self.client.post("/address/unspent-outputs", params, {"addresses": addresses}, cb);
536
};
537
538
/**
539
 * verify ownership of an address
540
 *
541
 * @param address       string      address hash
542
 * @param signature     string      a signed message (the address hash) using the private key of the address
543
 * @param [cb]          function    callback function to call when request is complete
544
 * @return q.Promise
545
 */
546
APIClient.prototype.verifyAddress = function(address, signature, cb) {
547
    var self = this;
548
549
    return self.client.post("/address/" + address + "/verify", null, {signature: signature}, cb);
550
};
551
552
/**
553
 * get all blocks (paginated)
554
 *
555
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
556
 * @param [cb]          function    callback function to call when request is complete
557
 * @return q.Promise
558
 */
559
APIClient.prototype.allBlocks = function(params, cb) {
560
    var self = this;
561
562
    if (typeof params === "function") {
563
        cb = params;
564
        params = null;
565
    }
566
567
    return self.client.get("/all-blocks", params, cb);
568
};
569
570
/**
571
 * get a block
572
 *
573
 * @param block         string|int  a block hash or a block height
574
 * @param [cb]          function    callback function to call when request is complete
575
 * @return q.Promise
576
 */
577
APIClient.prototype.block = function(block, cb) {
578
    var self = this;
579
580
    return self.client.get("/block/" + block, null, cb);
581
};
582
583
/**
584
 * get the latest block
585
 *
586
 * @param [cb]          function    callback function to call when request is complete
587
 * @return q.Promise
588
 */
589
APIClient.prototype.blockLatest = function(cb) {
590
    var self = this;
591
592
    return self.client.get("/block/latest", null, cb);
593
};
594
595
/**
596
 * get all transactions for a block (paginated)
597
 *
598
 * @param block         string|int  a block hash or a block height
599
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
600
 * @param [cb]          function    callback function to call when request is complete
601
 * @return q.Promise
602
 */
603
APIClient.prototype.blockTransactions = function(block, params, cb) {
604
    var self = this;
605
606
    if (typeof params === "function") {
607
        cb = params;
608
        params = null;
609
    }
610
611
    return self.client.get("/block/" + block + "/transactions", params, cb);
612
};
613
614
/**
615
 * get a single transaction
616
 *
617
 * @param tx            string      transaction hash
618
 * @param [cb]          function    callback function to call when request is complete
619
 * @return q.Promise
620
 */
621
APIClient.prototype.transaction = function(tx, cb) {
622
    var self = this;
623
624
    return self.client.get("/transaction/" + tx, null, cb);
625
};
626
627
/**
628
 * get a batch of transactions
629
 *
630
 * @param txs           string[]    list of transaction hashes (txId)
631
 * @param [cb]          function    callback function to call when request is complete
632
 * @return q.Promise
633
 */
634
APIClient.prototype.transactions = function(txs, cb) {
635
    var self = this;
636
637
    return self.client.post("/transactions", null, txs, cb, false);
638
};
639
640
/**
641
 * get a paginated list of all webhooks associated with the api user
642
 *
643
 * @param [params]      object      pagination: {page: 1, limit: 20}
644
 * @param [cb]          function    callback function to call when request is complete
645
 * @return q.Promise
646
 */
647
APIClient.prototype.allWebhooks = function(params, cb) {
648
    var self = this;
649
650
    if (typeof params === "function") {
651
        cb = params;
652
        params = null;
653
    }
654
655
    return self.client.get("/webhooks", params, cb);
656
};
657
658
/**
659
 * create a new webhook
660
 *
661
 * @param url           string      the url to receive the webhook events
662
 * @param [identifier]  string      a unique identifier associated with the webhook
663
 * @param [cb]          function    callback function to call when request is complete
664
 * @return q.Promise
665
 */
666
APIClient.prototype.setupWebhook = function(url, identifier, cb) {
667
    var self = this;
668
669
    if (typeof identifier === "function") {
670
        //mimic function overloading
671
        cb = identifier;
672
        identifier = null;
673
    }
674
675
    return self.client.post("/webhook", null, {url: url, identifier: identifier}, cb);
676
};
677
678
APIClient.prototype.getLegacyBitcoinCashAddress = function(input) {
679
    if (this.network === bitcoin.networks.bitcoincash || this.network === bitcoin.networks.bitcoincashtestnet) {
680
        var address;
681
        try {
682
            bitcoin.address.fromBase58Check(input, this.network);
683
            return input;
684
        } catch (e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
685
686
        address = bitcoin.address.fromCashAddress(input, this.network);
687
        var prefix;
688
        if (address.version === bitcoin.script.types.P2PKH) {
689
            prefix = this.network.pubKeyHash;
690
        } else if (address.version === bitcoin.script.types.P2SH) {
691
            prefix = this.network.scriptHash;
692
        } else {
693
            throw new Error("Unsupported address type");
694
        }
695
696
        return bitcoin.address.toBase58Check(address.hash, prefix);
697
    }
698
699
    throw new Error("Legacy addresses only work on bitcoin cash");
700
};
701
702
/**
703
 * get an existing webhook by it's identifier
704
 *
705
 * @param identifier    string      the unique identifier of the webhook to get
706
 * @param [cb]          function    callback function to call when request is complete
707
 * @return q.Promise
708
 */
709
APIClient.prototype.getWebhook = function(identifier, cb) {
710
    var self = this;
711
712
    return self.client.get("/webhook/" + identifier, null, cb);
713
};
714
715
/**
716
 * update an existing webhook
717
 *
718
 * @param identifier    string      the unique identifier of the webhook
719
 * @param webhookData   object      the data to update: {identifier: newIdentifier, url:newUrl}
720
 * @param [cb]          function    callback function to call when request is complete
721
 * @return q.Promise
722
 */
723
APIClient.prototype.updateWebhook = function(identifier, webhookData, cb) {
724
    var self = this;
725
726
    return self.client.put("/webhook/" + identifier, null, webhookData, cb);
727
};
728
729
/**
730
 * deletes an existing webhook and any event subscriptions associated with it
731
 *
732
 * @param identifier    string      the unique identifier of the webhook
733
 * @param [cb]          function    callback function to call when request is complete
734
 * @return q.Promise
735
 */
736
APIClient.prototype.deleteWebhook = function(identifier, cb) {
737
    var self = this;
738
739
    return self.client.delete("/webhook/" + identifier, null, null, cb);
740
};
741
742
/**
743
 * get a paginated list of all the events a webhook is subscribed to
744
 *
745
 * @param identifier    string      the unique identifier of the webhook
746
 * @param [params]      object      pagination: {page: 1, limit: 20}
747
 * @param [cb]          function    callback function to call when request is complete
748
 * @return q.Promise
749
 */
750
APIClient.prototype.getWebhookEvents = function(identifier, params, cb) {
751
    var self = this;
752
753
    if (typeof params === "function") {
754
        cb = params;
755
        params = null;
756
    }
757
758
    return self.client.get("/webhook/" + identifier + "/events", params, cb);
759
};
760
761
/**
762
 * subscribes a webhook to transaction events for a particular transaction
763
 *
764
 * @param identifier    string      the unique identifier of the webhook
765
 * @param transaction   string      the transaction hash
766
 * @param confirmations integer     the amount of confirmations to send
767
 * @param [cb]          function    callback function to call when request is complete
768
 * @return q.Promise
769
 */
770
APIClient.prototype.subscribeTransaction = function(identifier, transaction, confirmations, cb) {
771
    var self = this;
772
    var postData = {
773
        'event_type': 'transaction',
774
        'transaction': transaction,
775
        'confirmations': confirmations
776
    };
777
778
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
779
};
780
781
/**
782
 * subscribes a webhook to transaction events on a particular address
783
 *
784
 * @param identifier    string      the unique identifier of the webhook
785
 * @param address       string      the address hash
786
 * @param confirmations integer     the amount of confirmations to send
787
 * @param [cb]          function    callback function to call when request is complete
788
 * @return q.Promise
789
 */
790
APIClient.prototype.subscribeAddressTransactions = function(identifier, address, confirmations, cb) {
791
    var self = this;
792
    var postData = {
793
        'event_type': 'address-transactions',
794
        'address': address,
795
        'confirmations': confirmations
796
    };
797
798
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
799
};
800
801
/**
802
 * batch subscribes a webhook to multiple transaction events
803
 *
804
 * @param  identifier   string      the unique identifier of the webhook
805
 * @param  batchData    array       An array of objects containing batch event data:
806
 *                                  {address : 'address', confirmations : 'confirmations']
807
 *                                  where address is the address to subscribe to and confirmations (optional) is the amount of confirmations to send
808
 * @param [cb]          function    callback function to call when request is complete
809
 * @return q.Promise
810
 */
811
APIClient.prototype.batchSubscribeAddressTransactions = function(identifier, batchData, cb) {
812
    var self = this;
813
    batchData.forEach(function(record) {
814
        record.event_type = 'address-transactions';
815
    });
816
817
    return self.client.post("/webhook/" + identifier + "/events/batch", null, batchData, cb);
818
};
819
820
/**
821
 * subscribes a webhook to a new block event
822
 *
823
 * @param identifier    string      the unique identifier of the webhook
824
 * @param [cb]          function    callback function to call when request is complete
825
 * @return q.Promise
826
 */
827
APIClient.prototype.subscribeNewBlocks = function(identifier, cb) {
828
    var self = this;
829
    var postData = {
830
        'event_type': 'block'
831
    };
832
833
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
834
};
835
836
/**
837
 * removes an address transaction event subscription from a webhook
838
 *
839
 * @param identifier    string      the unique identifier of the webhook
840
 * @param address       string      the address hash
841
 * @param [cb]          function    callback function to call when request is complete
842
 * @return q.Promise
843
 */
844
APIClient.prototype.unsubscribeAddressTransactions = function(identifier, address, cb) {
845
    var self = this;
846
847
    return self.client.delete("/webhook/" + identifier + "/address-transactions/" + address, null, null, cb);
848
};
849
850
/**
851
 * removes an transaction event subscription from a webhook
852
 *
853
 * @param identifier    string      the unique identifier of the webhook
854
 * @param transaction   string      the transaction hash
855
 * @param [cb]          function    callback function to call when request is complete
856
 * @return q.Promise
857
 */
858
APIClient.prototype.unsubscribeTransaction = function(identifier, transaction, cb) {
859
    var self = this;
860
861
    return self.client.delete("/webhook/" + identifier + "/transaction/" + transaction, null, null, cb);
862
};
863
864
/**
865
 * removes a block event subscription from a webhook
866
 *
867
 * @param identifier    string      the unique identifier of the webhook
868
 * @param [cb]          function    callback function to call when request is complete
869
 * @return q.Promise
870
 */
871
APIClient.prototype.unsubscribeNewBlocks = function(identifier, cb) {
872
    var self = this;
873
874
    return self.client.delete("/webhook/" + identifier + "/block", null, null, cb);
875
};
876
877
/**
878
 * initialize an existing wallet
879
 *
880
 * Either takes two argument:
881
 * @param options       object      {}
882
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
883
 *
884
 * Or takes three arguments (old, deprecated syntax):
885
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
886
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
887
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 882. The second definition is ignored.
Loading history...
888
 *
889
 * @returns {q.Promise}
890
 */
891
APIClient.prototype.initWallet = function(options, cb) {
892
    var self = this;
893
894
    if (typeof options !== "object") {
895
        // get the old-style arguments
896
        options = {
897
            identifier: arguments[0],
898
            passphrase: arguments[1]
899
        };
900
901
        cb = arguments[2];
902
    }
903
904
    if (options.check_backup_key) {
905
        if (typeof options.check_backup_key !== "string") {
906
            throw new Error("Invalid input, must provide the backup key as a string (the xpub)");
907
        }
908
    }
909
910
    var deferred = q.defer();
911
    deferred.promise.spreadNodeify(cb);
912
913
    var identifier = options.identifier;
914
915
    if (!identifier) {
916
        deferred.reject(new blocktrail.WalletInitError("Identifier is required"));
917
        return deferred.promise;
918
    }
919
920
    deferred.resolve(self.client.get("/wallet/" + identifier, null, true).then(function(result) {
921
        var keyIndex = options.keyIndex || result.key_index;
922
923
        options.walletVersion = result.wallet_version;
924
925
        if (options.check_backup_key) {
926
            if (options.check_backup_key !== result.backup_public_key[0]) {
927
                throw new Error("Backup key returned from server didn't match our own copy");
928
            }
929
        }
930
        var backupPublicKey = bitcoin.HDNode.fromBase58(result.backup_public_key[0], self.network);
931
        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
932
            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
933
        });
934
        var primaryPublicKeys = _.mapValues(result.primary_public_keys, function(primaryPublicKey) {
935
            return bitcoin.HDNode.fromBase58(primaryPublicKey[0], self.network);
936
        });
937
938
        // initialize wallet
939
        var wallet = new Wallet(
940
            self,
941
            identifier,
942
            options.walletVersion,
943
            result.primary_mnemonic,
944
            result.encrypted_primary_seed,
945
            result.encrypted_secret,
946
            primaryPublicKeys,
947
            backupPublicKey,
948
            blocktrailPublicKeys,
949
            keyIndex,
950
            result.segwit || 0,
951
            self.testnet,
952
            result.checksum,
953
            result.upgrade_key_index,
954
            options.useCashAddress,
955
            options.bypassNewAddressCheck
956
        );
957
958
        wallet.recoverySecret = result.recovery_secret;
959
960
        if (!options.readOnly) {
961
            return wallet.unlock(options).then(function() {
962
                return wallet;
963
            });
964
        } else {
965
            return wallet;
966
        }
967
    }));
968
969
    return deferred.promise;
970
};
971
972
APIClient.CREATE_WALLET_PROGRESS_START = 0;
973
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET = 4;
974
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY = 5;
975
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY = 6;
976
APIClient.CREATE_WALLET_PROGRESS_PRIMARY = 10;
977
APIClient.CREATE_WALLET_PROGRESS_BACKUP = 20;
978
APIClient.CREATE_WALLET_PROGRESS_SUBMIT = 30;
979
APIClient.CREATE_WALLET_PROGRESS_INIT = 40;
980
APIClient.CREATE_WALLET_PROGRESS_DONE = 100;
981
982
/**
983
 * create a new wallet
984
 *   - will generate a new primary seed and backup seed
985
 *
986
 * Either takes two argument:
987
 * @param options       object      {}
988
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys) // nocommit @TODO
989
 *
990
 * For v1 wallets (explicitly specify options.walletVersion=v1):
991
 * @param options       object      {}
0 ignored issues
show
Documentation introduced by
The parameter options has already been documented on line 987. The second definition is ignored.
Loading history...
992
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 988. The second definition is ignored.
Loading history...
993
 *
994
 * Or takes four arguments (old, deprecated syntax):
995
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
996
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
997
 * @param keyIndex      int         override for the blocktrail cosign key to use (for development purposes)
0 ignored issues
show
Documentation introduced by
The parameter keyIndex does not exist. Did you maybe forget to remove this comment?
Loading history...
998
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 988. The second definition is ignored.
Loading history...
999
 * @returns {q.Promise}
1000
 */
1001
APIClient.prototype.createNewWallet = function(options, cb) {
1002
    /* jshint -W071, -W074 */
1003
1004
    var self = this;
1005
1006
    if (typeof options !== "object") {
1007
        // get the old-style arguments
1008
        var identifier = arguments[0];
1009
        var passphrase = arguments[1];
1010
        var keyIndex = arguments[2];
1011
        cb = arguments[3];
1012
1013
        // keyIndex is optional
1014
        if (typeof keyIndex === "function") {
1015
            cb = keyIndex;
1016
            keyIndex = null;
1017
        }
1018
1019
        options = {
1020
            identifier: identifier,
1021
            passphrase: passphrase,
1022
            keyIndex: keyIndex
1023
        };
1024
    }
1025
1026
    // default to v3
1027
    options.walletVersion = options.walletVersion || Wallet.WALLET_VERSION_V3;
1028
1029
    var deferred = q.defer();
1030
    deferred.promise.spreadNodeify(cb);
1031
1032
    q.nextTick(function() {
1033
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_START);
1034
1035
        options.keyIndex = options.keyIndex || 0;
1036
        options.passphrase = options.passphrase || options.password;
1037
        delete options.password;
1038
1039
        if (!options.identifier) {
1040
            deferred.reject(new blocktrail.WalletCreateError("Identifier is required"));
1041
            return deferred.promise;
1042
        }
1043
1044
        if (options.walletVersion === Wallet.WALLET_VERSION_V1) {
1045
            self._createNewWalletV1(options)
1046
                .progress(function(p) { deferred.notify(p); })
1047
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1048
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1049
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V2) {
1050
            self._createNewWalletV2(options)
1051
                .progress(function(p) { deferred.notify(p); })
1052
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1053
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1054
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V3) {
1055
            self._createNewWalletV3(options)
1056
                .progress(function(p) { deferred.notify(p); })
1057
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1058
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1059
        } else {
1060
            deferred.reject(new blocktrail.WalletCreateError("Invalid wallet version!"));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1061
        }
1062
    });
1063
1064
    return deferred.promise;
1065
};
1066
1067
APIClient.prototype._createNewWalletV1 = function(options) {
1068
    var self = this;
1069
1070
    var deferred = q.defer();
1071
1072
    q.nextTick(function() {
1073
1074
        if (!options.primaryMnemonic && !options.primarySeed) {
1075
            if (!options.passphrase && !options.password) {
1076
                deferred.reject(new blocktrail.WalletCreateError("Can't generate Primary Mnemonic without a passphrase"));
1077
                return deferred.promise;
1078
            } else {
1079
                options.primaryMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1080
                if (options.storePrimaryMnemonic !== false) {
1081
                    options.storePrimaryMnemonic = true;
1082
                }
1083
            }
1084
        }
1085
1086
        if (!options.backupMnemonic && !options.backupPublicKey) {
1087
            options.backupMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1088
        }
1089
1090
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
1091
1092
        self.resolvePrimaryPrivateKeyFromOptions(options)
1093
            .then(function(options) {
1094
                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
1095
1096
                return self.resolveBackupPublicKeyFromOptions(options)
1097
                    .then(function(options) {
1098
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
1099
1100
                        // create a checksum of our private key which we'll later use to verify we used the right password
1101
                        var pubKeyHash = bitcoin.crypto.hash160(options.primaryPrivateKey.getPublicKeyBuffer());
1102
                        var checksum = bitcoin.address.toBase58Check(pubKeyHash, self.network.pubKeyHash);
1103
                        var keyIndex = options.keyIndex;
1104
1105
                        var primaryPublicKey = options.primaryPrivateKey.deriveHardened(keyIndex).neutered();
1106
1107
                        // send the public keys to the server to store them
1108
                        //  and the mnemonic, which is safe because it's useless without the password
1109
                        return self.storeNewWalletV1(
1110
                            options.identifier,
1111
                            [primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1112
                            [options.backupPublicKey.toBase58(), "M"],
1113
                            options.storePrimaryMnemonic ? options.primaryMnemonic : false,
1114
                            checksum,
1115
                            keyIndex,
1116
                            options.segwit || null
1117
                        )
1118
                            .then(function(result) {
1119
                                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1120
1121
                                var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1122
                                    return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1123
                                });
1124
1125
                                var wallet = new Wallet(
1126
                                    self,
1127
                                    options.identifier,
1128
                                    Wallet.WALLET_VERSION_V1,
1129
                                    options.primaryMnemonic,
1130
                                    null,
1131
                                    null,
1132
                                    {keyIndex: primaryPublicKey},
1133
                                    options.backupPublicKey,
1134
                                    blocktrailPublicKeys,
1135
                                    keyIndex,
1136
                                    result.segwit || 0,
1137
                                    self.testnet,
1138
                                    checksum,
1139
                                    result.upgrade_key_index,
1140
                                    options.useCashAddress,
1141
                                    options.bypassNewAddressCheck
1142
                                );
1143
1144
                                return wallet.unlock({
1145
                                    walletVersion: Wallet.WALLET_VERSION_V1,
1146
                                    passphrase: options.passphrase,
1147
                                    primarySeed: options.primarySeed,
1148
                                    primaryMnemonic: null // explicit null
1149
                                }).then(function() {
1150
                                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1151
                                    return [
1152
                                        wallet,
1153
                                        {
1154
                                            walletVersion: wallet.walletVersion,
1155 View Code Duplication
                                            primaryMnemonic: options.primaryMnemonic,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1156
                                            backupMnemonic: options.backupMnemonic,
1157
                                            blocktrailPublicKeys: blocktrailPublicKeys
1158
                                        }
1159
                                    ];
1160
                                });
1161
                            });
1162
                    }
1163
                );
1164
            })
1165
            .then(
1166
            function(r) {
1167
                deferred.resolve(r);
1168
            },
1169
            function(e) {
1170
                deferred.reject(e);
1171
            }
1172
        )
1173
        ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1174
    });
1175
1176
    return deferred.promise;
1177
};
1178
1179
APIClient.prototype._createNewWalletV2 = function(options) {
1180
    var self = this;
1181
1182
    var deferred = q.defer();
1183
1184
    // avoid modifying passed options
1185
    options = _.merge({}, options);
1186
1187
    determineDataStorageV2_3(options)
1188
        .then(function(options) {
1189
            options.passphrase = options.passphrase || options.password;
1190
            delete options.password;
1191
1192
            // avoid deprecated options
1193
            if (options.primaryPrivateKey) {
1194
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1195
            }
1196
1197
            // seed should be provided or generated
1198
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1199
1200
            return options;
1201
        })
1202
        .then(function(options) {
1203
            return produceEncryptedDataV2(options, deferred.notify.bind(deferred));
1204
        })
1205
        .then(function(options) {
1206
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1207
        })
1208
        .then(function(options) {
1209
            // create a checksum of our private key which we'll later use to verify we used the right password
1210
            var pubKeyHash = bitcoin.crypto.hash160(options.primaryPrivateKey.getPublicKeyBuffer());
1211
            var checksum = bitcoin.address.toBase58Check(pubKeyHash, self.network.pubKeyHash);
1212
            var keyIndex = options.keyIndex;
1213
1214
            // send the public keys and encrypted data to server
1215
            return self.storeNewWalletV2(
1216
                options.identifier,
1217
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1218
                [options.backupPublicKey.toBase58(), "M"],
1219
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1220
                options.storeDataOnServer ? options.encryptedSecret : false,
1221
                options.storeDataOnServer ? options.recoverySecret : false,
1222
                checksum,
1223
                keyIndex,
1224
                options.support_secret || null,
1225
                options.segwit || null
1226
            )
1227
                .then(
1228
                function(result) {
1229
                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1230
1231
                    var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1232
                        return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1233
                    });
1234
1235
                    var wallet = new Wallet(
1236
                        self,
1237
                        options.identifier,
1238
                        Wallet.WALLET_VERSION_V2,
1239
                        null,
1240
                        options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1241
                        options.storeDataOnServer ? options.encryptedSecret : null,
1242
                        {keyIndex: options.primaryPublicKey},
1243
                        options.backupPublicKey,
1244
                        blocktrailPublicKeys,
1245
                        keyIndex,
1246
                        result.segwit || 0,
1247
                        self.testnet,
1248
                        checksum,
1249
                        result.upgrade_key_index,
1250
                        options.useCashAddress,
1251
                        options.bypassNewAddressCheck
1252
                    );
1253
1254
                    // pass along decrypted data to avoid extra work
1255
                    return wallet.unlock({
1256
                        walletVersion: Wallet.WALLET_VERSION_V2,
1257
                        passphrase: options.passphrase,
1258
                        primarySeed: options.primarySeed,
1259
                        secret: options.secret
1260
                    }).then(function() {
1261
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1262
                        return [
1263
                            wallet,
1264 View Code Duplication
                            {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1265
                                walletVersion: wallet.walletVersion,
1266
                                encryptedPrimarySeed: options.encryptedPrimarySeed ?
1267
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedPrimarySeed, 'base64', 'hex')) :
1268
                                    null,
1269
                                backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed.toString('hex')) : null,
1270
                                recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1271
                                    bip39.entropyToMnemonic(blocktrail.convert(options.recoveryEncryptedSecret, 'base64', 'hex')) :
1272
                                    null,
1273
                                encryptedSecret: options.encryptedSecret ?
1274
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedSecret, 'base64', 'hex')) :
1275
                                    null,
1276
                                blocktrailPublicKeys: blocktrailPublicKeys
1277
                            }
1278
                        ];
1279
                    });
1280
                }
1281
            );
1282
        })
1283
       .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1284
1285
    return deferred.promise;
1286
};
1287
1288
APIClient.prototype._createNewWalletV3 = function(options) {
1289
    var self = this;
1290
1291
    var deferred = q.defer();
1292
1293
    // avoid modifying passed options
1294
    options = _.merge({}, options);
1295
1296
    determineDataStorageV2_3(options)
1297
        .then(function(options) {
1298
            options.passphrase = options.passphrase || options.password;
1299
            delete options.password;
1300
1301
            // avoid deprecated options
1302
            if (options.primaryPrivateKey) {
1303
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1304
            }
1305
1306
            // seed should be provided or generated
1307
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1308
1309
            return options;
1310
        })
1311
        .then(function(options) {
1312
            return self.produceEncryptedDataV3(options, deferred.notify.bind(deferred));
1313
        })
1314
        .then(function(options) {
1315
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1316
        })
1317
        .then(function(options) {
1318
            // create a checksum of our private key which we'll later use to verify we used the right password
1319
            var pubKeyHash = bitcoin.crypto.hash160(options.primaryPrivateKey.getPublicKeyBuffer());
1320
            var checksum = bitcoin.address.toBase58Check(pubKeyHash, self.network.pubKeyHash);
1321
            var keyIndex = options.keyIndex;
1322
1323
            // send the public keys and encrypted data to server
1324
            return self.storeNewWalletV3(
1325
                options.identifier,
1326
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1327
                [options.backupPublicKey.toBase58(), "M"],
1328
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1329
                options.storeDataOnServer ? options.encryptedSecret : false,
1330
                options.storeDataOnServer ? options.recoverySecret : false,
1331
                checksum,
1332
                keyIndex,
1333
                options.support_secret || null,
1334
                options.segwit || null
1335
            )
1336
                .then(
1337
                    // result, deferred, self(apiclient)
1338
                    function(result) {
1339
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1340
1341
                        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1342
                            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1343
                        });
1344
1345
                        var wallet = new Wallet(
1346
                            self,
1347
                            options.identifier,
1348
                            Wallet.WALLET_VERSION_V3,
1349
                            null,
1350
                            options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1351
                            options.storeDataOnServer ? options.encryptedSecret : null,
1352
                            {keyIndex: options.primaryPublicKey},
1353
                            options.backupPublicKey,
1354
                            blocktrailPublicKeys,
1355
                            keyIndex,
1356
                            result.segwit || 0,
1357
                            self.testnet,
1358
                            checksum,
1359
                            result.upgrade_key_index,
1360
                            options.useCashAddress,
1361
                            options.bypassNewAddressCheck
1362
                        );
1363
1364
                        // pass along decrypted data to avoid extra work
1365
                        return wallet.unlock({
1366
                            walletVersion: Wallet.WALLET_VERSION_V3,
1367
                            passphrase: options.passphrase,
1368
                            primarySeed: options.primarySeed,
1369
                            secret: options.secret
1370
                        }).then(function() {
1371
                            deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1372
                            return [
1373
                                wallet,
1374
                                {
1375
                                    walletVersion: wallet.walletVersion,
1376
                                    encryptedPrimarySeed: options.encryptedPrimarySeed ? EncryptionMnemonic.encode(options.encryptedPrimarySeed) : null,
1377
                                    backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed) : null,
1378
                                    recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1379
                                        EncryptionMnemonic.encode(options.recoveryEncryptedSecret) : null,
1380
                                    encryptedSecret: options.encryptedSecret ? EncryptionMnemonic.encode(options.encryptedSecret) : null,
1381
                                    blocktrailPublicKeys: blocktrailPublicKeys
1382
                                }
1383
                            ];
1384
                        });
1385
                    }
1386
                );
1387
        })
1388
        .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1389
1390
    return deferred.promise;
1391
};
1392
1393
function verifyPublicBip32Key(bip32Key, network) {
1394
    var hk = bitcoin.HDNode.fromBase58(bip32Key[0], network);
1395
    if (typeof hk.keyPair.d !== "undefined") {
1396
        throw new Error('BIP32Key contained private key material - abort');
1397
    }
1398
1399
    if (bip32Key[1].slice(0, 1) !== "M") {
1400
        throw new Error("BIP32Key contained non-public path - abort");
1401
    }
1402
}
1403
1404
function verifyPublicOnly(walletData, network) {
1405
    verifyPublicBip32Key(walletData.primary_public_key, network);
1406
    verifyPublicBip32Key(walletData.backup_public_key, network);
1407
}
1408
1409
/**
1410
 * create wallet using the API
1411
 *
1412
 * @param identifier            string      the wallet identifier to create
1413
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1414
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1415
 * @param primaryMnemonic       string      mnemonic to store
1416
 * @param checksum              string      checksum to store
1417
 * @param keyIndex              int         keyIndex that was used to create wallet
1418
 * @param segwit                bool
1419
 * @returns {q.Promise}
1420
 */
1421
APIClient.prototype.storeNewWalletV1 = function(identifier, primaryPublicKey, backupPublicKey, primaryMnemonic,
1422
                                                checksum, keyIndex, segwit) {
1423
    var self = this;
1424
1425
    var postData = {
1426
        identifier: identifier,
1427
        wallet_version: Wallet.WALLET_VERSION_V1,
1428
        primary_public_key: primaryPublicKey,
1429
        backup_public_key: backupPublicKey,
1430
        primary_mnemonic: primaryMnemonic,
1431
        checksum: checksum,
1432
        key_index: keyIndex,
1433
        segwit: segwit
1434
    };
1435
1436
    verifyPublicOnly(postData, self.network);
1437
1438
    return self.client.post("/wallet", null, postData);
1439
};
1440
1441
/**
1442
 * create wallet using the API
1443
 *
1444
 * @param identifier            string      the wallet identifier to create
1445
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1446
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1447
 * @param encryptedPrimarySeed  string      openssl format
1448
 * @param encryptedSecret       string      openssl format
1449
 * @param recoverySecret        string      openssl format
1450
 * @param checksum              string      checksum to store
1451
 * @param keyIndex              int         keyIndex that was used to create wallet
1452
 * @param supportSecret         string
1453
 * @param segwit                bool
1454
 * @returns {q.Promise}
1455
 */
1456
APIClient.prototype.storeNewWalletV2 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1457
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1458
    var self = this;
1459
1460
    var postData = {
1461
        identifier: identifier,
1462
        wallet_version: Wallet.WALLET_VERSION_V2,
1463
        primary_public_key: primaryPublicKey,
1464
        backup_public_key: backupPublicKey,
1465
        encrypted_primary_seed: encryptedPrimarySeed,
1466
        encrypted_secret: encryptedSecret,
1467
        recovery_secret: recoverySecret,
1468
        checksum: checksum,
1469
        key_index: keyIndex,
1470
        support_secret: supportSecret || null,
1471
        segwit: segwit
1472
    };
1473
1474
    verifyPublicOnly(postData, self.network);
1475
1476
    return self.client.post("/wallet", null, postData);
1477
};
1478
1479
/**
1480
 * create wallet using the API
1481
 *
1482
 * @param identifier            string      the wallet identifier to create
1483
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1484
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1485
 * @param encryptedPrimarySeed  Buffer      buffer of ciphertext
1486
 * @param encryptedSecret       Buffer      buffer of ciphertext
1487
 * @param recoverySecret        Buffer      buffer of recovery secret
1488
 * @param checksum              string      checksum to store
1489
 * @param keyIndex              int         keyIndex that was used to create wallet
1490
 * @param supportSecret         string
1491
 * @param segwit                bool
1492
 * @returns {q.Promise}
1493
 */
1494
APIClient.prototype.storeNewWalletV3 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1495
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1496
    var self = this;
1497
1498
    var postData = {
1499
        identifier: identifier,
1500
        wallet_version: Wallet.WALLET_VERSION_V3,
1501
        primary_public_key: primaryPublicKey,
1502
        backup_public_key: backupPublicKey,
1503
        encrypted_primary_seed: encryptedPrimarySeed.toString('base64'),
1504
        encrypted_secret: encryptedSecret.toString('base64'),
1505
        recovery_secret: recoverySecret.toString('hex'),
1506
        checksum: checksum,
1507
        key_index: keyIndex,
1508
        support_secret: supportSecret || null,
1509
        segwit: segwit
1510
    };
1511
1512
    verifyPublicOnly(postData, self.network);
1513
1514
    return self.client.post("/wallet", null, postData);
1515
};
1516
1517
/**
1518
 * create wallet using the API
1519
 *
1520
 * @param identifier            string      the wallet identifier to create
1521
 * @param postData              object
1522
 * @param [cb]                  function    callback(err, result)
1523
 * @returns {q.Promise}
1524
 */
1525
APIClient.prototype.updateWallet = function(identifier, postData, cb) {
1526
    var self = this;
1527
1528
    return self.client.post("/wallet/" + identifier, null, postData, cb);
1529
};
1530
1531
/**
1532
 * upgrade wallet to use a new account number
1533
 *  the account number specifies which blocktrail cosigning key is used
1534
 *
1535
 * @param identifier            string      the wallet identifier
1536
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1537
 * @param keyIndex              int         keyIndex that was used to create wallet
1538
 * @param [cb]                  function    callback(err, result)
1539
 * @returns {q.Promise}
1540
 */
1541
APIClient.prototype.upgradeKeyIndex = function(identifier, keyIndex, primaryPublicKey, cb) {
1542
    var self = this;
1543
1544
    return self.client.post("/wallet/" + identifier + "/upgrade", null, {
1545
        key_index: keyIndex,
1546
        primary_public_key: primaryPublicKey
1547
    }, cb);
1548
};
1549
1550
/**
1551
 * get the balance for the wallet
1552
 *
1553
 * @param identifier            string      the wallet identifier
1554
 * @param [cb]                  function    callback(err, result)
1555
 * @returns {q.Promise}
1556
 */
1557
APIClient.prototype.getWalletBalance = function(identifier, cb) {
1558
    var self = this;
1559
1560
    return self.client.get("/wallet/" + identifier + "/balance", null, true, cb);
1561
};
1562
1563
/**
1564
 * do HD wallet discovery for the wallet
1565
 *
1566
 * @param identifier            string      the wallet identifier
1567
 * @param [cb]                  function    callback(err, result)
1568
 * @returns {q.Promise}
1569
 */
1570
APIClient.prototype.doWalletDiscovery = function(identifier, gap, cb) {
1571
    var self = this;
1572
1573
    return self.client.get("/wallet/" + identifier + "/discovery", {gap: gap}, true, cb);
1574
};
1575
1576
1577
/**
1578
 * get a new derivation number for specified parent path
1579
 *  eg; m/44'/1'/0/0 results in m/44'/1'/0/0/0 and next time in m/44'/1'/0/0/1 and next time in m/44'/1'/0/0/2
1580
 *
1581
 * @param identifier            string      the wallet identifier
1582
 * @param path                  string      the parent path for which to get a new derivation,
1583
 *                                           can be suffixed with /* to make it clear on which level the derivations hould be
1584
 * @param [cb]                  function    callback(err, result)
1585
 * @returns {q.Promise}
1586
 */
1587
APIClient.prototype.getNewDerivation = function(identifier, path, cb) {
1588
    var self = this;
1589
1590
    return self.client.post("/wallet/" + identifier + "/path", null, {path: path}, cb);
1591
};
1592
1593
1594
/**
1595
 * delete the wallet
1596
 *  the checksum address and a signature to verify you ownership of the key of that checksum address
1597
 *  is required to be able to delete a wallet
1598
 *
1599
 * @param identifier            string      the wallet identifier
1600
 * @param checksumAddress       string      the address for your master private key (and the checksum used when creating the wallet)
1601
 * @param checksumSignature     string      a signature of the checksum address as message signed by the private key matching that address
1602
 * @param [force]               bool        ignore warnings (such as a non-zero balance)
1603
 * @param [cb]                  function    callback(err, result)
1604
 * @returns {q.Promise}
1605
 */
1606
APIClient.prototype.deleteWallet = function(identifier, checksumAddress, checksumSignature, force, cb) {
1607
    var self = this;
1608
1609
    if (typeof force === "function") {
1610
        cb = force;
1611
        force = false;
1612
    }
1613
1614
    return self.client.delete("/wallet/" + identifier, {force: force}, {
1615
        checksum: checksumAddress,
1616
        signature: checksumSignature
1617
    }, cb);
1618
};
1619
1620
/**
1621
 * use the API to get the best inputs to use based on the outputs
1622
 *
1623
 * the return array has the following format:
1624
 * [
1625
 *  "utxos" => [
1626
 *      [
1627
 *          "hash" => "<txHash>",
1628
 *          "idx" => "<index of the output of that <txHash>",
1629
 *          "scriptpubkey_hex" => "<scriptPubKey-hex>",
1630
 *          "value" => 32746327,
1631
 *          "address" => "1address",
1632
 *          "path" => "m/44'/1'/0'/0/13",
1633
 *          "redeem_script" => "<redeemScript-hex>",
1634
 *      ],
1635
 *  ],
1636
 *  "fee"   => 10000,
1637
 *  "change"=> 1010109201,
1638
 * ]
1639
 *
1640
 * @param identifier        string      the wallet identifier
1641
 * @param pay               array       {'address': (int)value}     coins to send
1642
 * @param lockUTXO          bool        lock UTXOs for a few seconds to allow for transaction to be created
1643
 * @param allowZeroConf     bool        allow zero confirmation unspent outputs to be used in coin selection
1644
 * @param feeStrategy       string      defaults to
1645
 * @param options
1646
 * @param [cb]              function    callback(err, utxos, fee, change)
1647
 * @returns {q.Promise}
1648
 */
1649
APIClient.prototype.coinSelection = function(identifier, pay, lockUTXO, allowZeroConf, feeStrategy, options, cb) {
1650
    var self = this;
1651
1652
    if (typeof feeStrategy === "function") {
1653
        cb = feeStrategy;
1654
        feeStrategy = null;
1655
        options = {};
1656
    } else if (typeof options === "function") {
1657
        cb = options;
1658
        options = {};
1659
    }
1660
1661
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1662
    options = options || {};
1663
1664
    var deferred = q.defer();
1665
    deferred.promise.spreadNodeify(cb);
1666
1667
    var params = {
1668
        lock: lockUTXO,
1669
        zeroconf: allowZeroConf ? 1 : 0,
1670
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1671
        fee_strategy: feeStrategy
1672
    };
1673
1674
    if (options.forcefee) {
1675
        params['forcefee'] = options.forcefee;
1676
    }
1677
1678
    deferred.resolve(
1679
        self.client.post("/wallet/" + identifier + "/coin-selection", params, pay).then(
1680
            function(result) {
1681
                return [result.utxos, result.fee, result.change, result];
1682
            },
1683
            function(err) {
1684
                if (err.message.match(/too low to pay the fee/)) {
1685
                    throw blocktrail.WalletFeeError(err);
1686
                }
1687
1688
                throw err;
1689
            }
1690
        )
1691
    );
1692
1693
    return deferred.promise;
1694
};
1695
1696
/**
1697
 * @param [cb]              function    callback(err, utxos, fee, change)
1698
 * @returns {q.Promise}
1699
 */
1700
APIClient.prototype.feePerKB = function(cb) {
1701
    var self = this;
1702
1703
    var deferred = q.defer();
1704
    deferred.promise.spreadNodeify(cb);
1705
1706
    deferred.resolve(self.client.get("/fee-per-kb"));
1707
1708
    return deferred.promise;
1709
};
1710
1711
/**
1712
 * send the transaction using the API
1713
 *
1714
 * @param identifier        string      the wallet identifier
1715
 * @param txHex             string      partially signed transaction as hex string
1716
 * @param paths             array       list of paths used in inputs which should be cosigned by the API
1717
 * @param checkFee          bool        when TRUE the API will verify if the fee is 100% correct and otherwise throw an exception
1718
 * @param [twoFactorToken]  string      2FA token
1719
 * @param [prioboost]       bool
1720
 * @param [cb]              function    callback(err, txHash)
1721
 * @returns {q.Promise}
1722
 */
1723
APIClient.prototype.sendTransaction = function(identifier, txHex, paths, checkFee, twoFactorToken, prioboost, cb) {
1724
    var self = this;
1725
1726
    if (typeof twoFactorToken === "function") {
1727
        cb = twoFactorToken;
1728
        twoFactorToken = null;
1729
        prioboost = false;
1730
    } else if (typeof prioboost === "function") {
1731
        cb = prioboost;
1732
        prioboost = false;
1733
    }
1734
1735
    var data = {
1736
        paths: paths,
1737
        two_factor_token: twoFactorToken
1738
    };
1739
    if (typeof txHex === "string") {
1740
        data.raw_transaction = txHex;
1741
    } else if (typeof txHex === "object") {
1742
        Object.keys(txHex).map(function(key) {
1743
            data[key] = txHex[key];
1744
        });
1745
    }
1746
1747
    return self.client.post(
1748
        "/wallet/" + identifier + "/send",
1749
        {
1750
            check_fee: checkFee ? 1 : 0,
1751
            prioboost: prioboost ? 1 : 0
1752
        },
1753
        data,
1754
        cb
1755
    );
1756
};
1757
1758
/**
1759
 * setup a webhook for this wallet
1760
 *
1761
 * @param identifier        string      the wallet identifier
1762
 * @param webhookIdentifier string      identifier for the webhook
1763
 * @param url               string      URL to receive webhook events
1764
 * @param [cb]              function    callback(err, webhook)
1765
 * @returns {q.Promise}
1766
 */
1767
APIClient.prototype.setupWalletWebhook = function(identifier, webhookIdentifier, url, cb) {
1768
    var self = this;
1769
1770
    return self.client.post("/wallet/" + identifier + "/webhook", null, {url: url, identifier: webhookIdentifier}, cb);
1771
};
1772
1773
/**
1774
 * delete a webhook that was created for this wallet
1775
 *
1776
 * @param identifier        string      the wallet identifier
1777
 * @param webhookIdentifier string      identifier for the webhook
1778
 * @param [cb]              function    callback(err, success)
1779
 * @returns {q.Promise}
1780
 */
1781
APIClient.prototype.deleteWalletWebhook = function(identifier, webhookIdentifier, cb) {
1782
    var self = this;
1783
1784
    return self.client.delete("/wallet/" + identifier + "/webhook/" + webhookIdentifier, null, null, cb);
1785
};
1786
1787
/**
1788
 * get all transactions for an wallet (paginated)
1789
 *
1790
 * @param identifier    string      wallet identifier
1791
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1792
 * @param [cb]          function    callback function to call when request is complete
1793
 * @return q.Promise
1794
 */
1795
APIClient.prototype.walletTransactions = function(identifier, params, cb) {
1796
    var self = this;
1797
1798
    if (typeof params === "function") {
1799
        cb = params;
1800
        params = null;
1801
    }
1802
1803
    return self.client.get("/wallet/" + identifier + "/transactions", params, true, cb);
1804
};
1805
1806
/**
1807
 * get all addresses for an wallet (paginated)
1808
 *
1809
 * @param identifier    string      wallet identifier
1810
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1811
 * @param [cb]          function    callback function to call when request is complete
1812
 * @return q.Promise
1813
 */
1814
APIClient.prototype.walletAddresses = function(identifier, params, cb) {
1815
    var self = this;
1816
1817
    if (typeof params === "function") {
1818
        cb = params;
1819
        params = null;
1820
    }
1821
1822
    return self.client.get("/wallet/" + identifier + "/addresses", params, true, cb);
1823
};
1824
1825
/**
1826
 * @param identifier    string      wallet identifier
1827
 * @param address       string      the address to label
1828
 * @param label         string      the label
1829
 * @param [cb]          function    callback(err, res)
1830
 * @return q.Promise
1831
 */
1832
APIClient.prototype.labelWalletAddress = function(identifier, address, label, cb) {
1833
    var self = this;
1834
1835
    return self.client.post("/wallet/" + identifier + "/address/" + address + "/label", null, {label: label}, cb);
1836
};
1837
1838
APIClient.prototype.walletMaxSpendable = function(identifier, allowZeroConf, feeStrategy, options, cb) {
1839
    var self = this;
1840
1841
    if (typeof feeStrategy === "function") {
1842
        cb = feeStrategy;
1843
        feeStrategy = null;
1844
    } else if (typeof options === "function") {
1845
        cb = options;
1846
        options = {};
1847
    }
1848
1849
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1850
    options = options || {};
1851
1852
    var params = {
1853
        outputs: options.outputs ? options.outputs : 1,
1854
        zeroconf: allowZeroConf ? 1 : 0,
1855
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1856
        fee_strategy: feeStrategy
1857
    };
1858
1859
    if (options.forcefee) {
1860
        params['forcefee'] = options.forcefee;
1861
    }
1862
1863
    return self.client.get("/wallet/" + identifier + "/max-spendable", params, true, cb);
1864
};
1865
1866
/**
1867
 * get all UTXOs for an wallet (paginated)
1868
 *
1869
 * @param identifier    string      wallet identifier
1870
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1871
 * @param [cb]          function    callback function to call when request is complete
1872
 * @return q.Promise
1873
 */
1874
APIClient.prototype.walletUTXOs = function(identifier, params, cb) {
1875
    var self = this;
1876
1877
    if (typeof params === "function") {
1878
        cb = params;
1879
        params = null;
1880
    }
1881
1882
    return self.client.get("/wallet/" + identifier + "/utxos", params, true, cb);
1883
};
1884
1885
/**
1886
 * get a paginated list of all wallets associated with the api user
1887
 *
1888
 * @param [params]      object      pagination: {page: 1, limit: 20}
1889
 * @param [cb]          function    callback function to call when request is complete
1890
 * @return q.Promise
1891
 */
1892
APIClient.prototype.allWallets = function(params, cb) {
1893
    var self = this;
1894
1895
    if (typeof params === "function") {
1896
        cb = params;
1897
        params = null;
1898
    }
1899
1900
    return self.client.get("/wallets", params, true, cb);
1901
};
1902
1903
/**
1904
 * verify a message signed bitcoin-core style
1905
 *
1906
 * @param message        string
1907
 * @param address        string
1908
 * @param signature      string
1909
 * @param [cb]          function    callback function to call when request is complete
1910
 * @return q.Promise
1911
 */
1912
APIClient.prototype.verifyMessage = function(message, address, signature, cb) {
1913
    var self = this;
1914
1915
    // we could also use the API instead of the using bitcoinjs-lib to verify
1916
    // return self.client.post("/verify_message", null, {message: message, address: address, signature: signature}, cb);
1917
1918
    var deferred = q.defer();
1919
    deferred.promise.nodeify(cb);
1920
    try {
1921
        var result = bitcoinMessage.verify(address, self.network.messagePrefix, message, new Buffer(signature, 'base64'));
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1922
        deferred.resolve(result);
1923
    } catch (e) {
1924
        deferred.reject(e);
1925
    }
1926
1927
    return deferred.promise;
1928
};
1929
1930
/**
1931
 * max is 0.001
1932
 * testnet only
1933
 *
1934
 * @param address
1935
 * @param amount
1936
 * @param cb
1937
 */
1938
APIClient.prototype.faucetWithdrawl = function(address, amount, cb) {
1939
    var self = this;
1940
1941
    return self.client.post("/faucet/withdrawl", null, {address: address, amount: amount}, cb);
1942
};
1943
1944
/**
1945
 * send a raw transaction
1946
 *
1947
 * @param rawTransaction    string      raw transaction as HEX
1948
 * @param [cb]              function    callback function to call when request is complete
1949
 * @return q.Promise
1950
 */
1951
APIClient.prototype.sendRawTransaction = function(rawTransaction, cb) {
1952
    var self = this;
1953
1954
    return self.client.post("/send-raw-tx", null, rawTransaction, cb);
1955
};
1956
1957
/**
1958
 * get the current price index
1959
 *
1960
 * @param [cb]          function    callback({'USD': 287.30})
1961
 * @return q.Promise
1962
 */
1963
APIClient.prototype.price = function(cb) {
1964
    var self = this;
1965
1966
    return self.client.get("/price", null, false, cb);
1967
};
1968
1969
module.exports = APIClient;
1970